package com.zrkizzy.seckill.interceptor;

import com.zrkizzy.seckill.annotation.AccessLimit;
import com.zrkizzy.seckill.config.mapper.JacksonObjectMapper;
import com.zrkizzy.seckill.entity.User;
import com.zrkizzy.seckill.enums.HttpStatusEnum;
import com.zrkizzy.seckill.service.core.IUserService;
import com.zrkizzy.seckill.service.tool.IRedisService;
import com.zrkizzy.seckill.utils.CookieUtil;
import com.zrkizzy.seckill.vo.common.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

import static com.zrkizzy.seckill.constant.CommonConst.*;

/**
 * 接口限流拦截器
 *
 * @author zhangrongkang
 * @since 2023/4/12
 */
@Component
public class AccessLimitInterceptor implements HandlerInterceptor {
    @Autowired
    private IRedisService redisService;
    @Autowired
    private IUserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 判断要处理的内容是否为方法
        if (handler instanceof HandlerMethod method) {
            // 获取当前请求中的用户
            User user = getLoginUser(request, response);
            // 获取到当前方法上的注解
            AccessLimit accessLimit = method.getMethodAnnotation(AccessLimit.class);
            if (null == accessLimit) {
                // 如果没有注解则直接放行
                return true;
            }
            // 获取到注解中对应的参数
            int second = accessLimit.second();
            int maxCount = accessLimit.maxCount();
            boolean needLogin = accessLimit.needLogin();

            // 指定当前用户访问路径中Redis存储的Key
            StringBuilder key = new StringBuilder().append(request.getRequestURI());
            // 如果需要登录
            if (needLogin) {
                // 用户为空则返回错误
                if (null == user) {
                    render(response, HttpStatusEnum.USER_EXIST_ERROR);
                    // 拦截掉当前请求
                    return false;
                }
                // 拼接完整的Key
                key.append(":").append(user.getId());
            }
            String redisKey = key.toString();
            // 获取Redis中存储的访问次数
            Integer redisCount = redisService.get(redisKey, Integer.class);
            if (null == redisCount) {
                // 为空则设置当前Key到Redis中
                redisService.set(redisKey, 0, second);
            } else if (redisCount < maxCount) {
                // 没有到达最大访问次数，访问次数自增
                redisService.increment(redisKey);
            } else {
                render(response, HttpStatusEnum.ACCESS_LIMIT);
                // 不允许请求通过
                return false;
            }
        }
        return true;
    }

    /**
     * 构建返回对象
     *
     * @param response response响应
     */
    private void render(HttpServletResponse response, HttpStatusEnum httpStatusEnum) throws IOException {
        response.setContentType(APPLICATION_JSON);
        response.setCharacterEncoding(UTF_8);
        PrintWriter writer = response.getWriter();
        Result result = Result.error(httpStatusEnum);
        writer.write(new JacksonObjectMapper().writeValueAsString(result));
    }

    /**
     * 获取当前登录用户
     *
     * @param request request请求
     * @param response response响应
     * @return 用户对象
     */
    private User getLoginUser(HttpServletRequest request, HttpServletResponse response) {
        // 获取当前请求中的用户
        String userTicket = CookieUtil.getCookieValue(request, USER_TICKET);
        // 判断签名是否存在
        if (StringUtils.hasLength(userTicket)) {
            return userService.getUserByCookie(userTicket, request, response);
        }
        return null;
    }
}
